﻿using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CoreHelper
{
    /// <summary>
    /// 内存缓存
    /// </summary>
    public class MemoryCacheHelper
    {
        #region 变量与属性
        /// <summary>
        /// 锁
        /// </summary>
        private static readonly Object __memoryCache_locker = new object();
        /// <summary>
        /// 缓存对象
        /// </summary>
        private static IMemoryCache _memoryCache = new MemoryCache(new MemoryCacheOptions());
        #endregion

        #region 删除缓存
        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static long Del(params string[] key)
        {
            foreach (var k in key)
            {
                _memoryCache.Remove(k);
            }
            return key.Length;
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static Task<long> DelAsync(params string[] key)
        {
            foreach (var k in key)
            {
                _memoryCache.Remove(k);
            }

            return Task.FromResult((long)key.Length);
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="pattern">缓存key模板匹配规则,大小写敏感</param>
        /// <returns></returns>
        public static long DelByPattern(string pattern)
        {
            if (string.IsNullOrEmpty(pattern))
                return default;

            var cacheKeys = GetCacheKeys();//获取所有缓存键
            var keys = cacheKeys.Where(k => Regex.IsMatch(k, pattern));

            if (keys != null && keys.Count() > 0)
            {
                return Del(keys.ToArray());
            }

            return default;
            /*
            调用示例：
            "CaCheTest*"//删除所有以CaCheTest开头的缓存
            "*1"//删除所有以1结尾的缓存
            "[Cm]*"//删除所有第一个字符为大写C或小写m的缓存
            "[m]*[1]"//删除所有以m开头，以1结尾的缓存
            ".*abc.*"//删除所有包含abc的缓存
            更多用法请自定义正则表达式实现
            string key1 = "CaCheTest*";
            long _count = await CoreHelper.RedisCacheHelper.DelByPatternAsync(key1);
             */
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="pattern">模糊匹配包含某个关键字的缓存key</param>
        /// <returns></returns>
        public static async Task<long> DelByPatternAsync(string pattern)
        {
            if (string.IsNullOrEmpty(pattern))
                return default;

            var cacheKeys = GetCacheKeys();//获取所有缓存键
            var keys = cacheKeys.Where(k => Regex.IsMatch(k, pattern));

            if (keys != null && keys.Count() > 0)
            {
                return await DelAsync(keys.ToArray());
            }

            return default;
            /*
            调用示例：
            "CaCheTest*"//删除所有以CaCheTest开头的缓存
            "*1"//删除所有以1结尾的缓存
            "[Cm]*"//删除所有第一个字符为大写C或小写m的缓存
            "[m]*[1]"//删除所有以m开头，以1结尾的缓存
            ".*abc.*"//删除所有包含abc的缓存
            更多用法请自定义正则表达式实现
            string key1 = "CaCheTest*";
            long _count = await CoreHelper.RedisCacheHelper.DelByPatternAsync(key1);
             */
        }
        #endregion

        #region 检测指定键是否存在
        /// <summary>
        /// 检测指定键是否存在
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static bool Exists(string key)
        {
            return _memoryCache.TryGetValue(key, out _);
        }

        /// <summary>
        /// 检测指定键是否存在
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static Task<bool> ExistsAsync(string key)
        {
            return Task.FromResult(_memoryCache.TryGetValue(key, out _));
        }
        #endregion

        #region 获取缓存
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static string Get(string key)
        {
            return _memoryCache.Get(key)?.ToString();
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            return _memoryCache.Get<T>(key);
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static Task<string> GetAsync(string key)
        {
            return Task.FromResult(Get(key));
        }

        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <typeparam name="T">获取缓存</typeparam>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public static Task<T> GetAsync<T>(string key)
        {
            return Task.FromResult(Get<T>(key));
        }
        #endregion

        #region 设置缓存
        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <param name="value">缓存value</param>
        public static void Set(string key, object value)
        {
            _memoryCache.Set(key, value);
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <param name="value">缓存value</param>
        /// <param name="expire">过期时间TimeSpan.FromMinutes(60)</param>
        public static void Set(string key, object value, TimeSpan expire)
        {
            _memoryCache.Set(key, value, expire);
        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <param name="value">缓存value</param>
        /// <param name="expire">过期时间，例如TimeSpan.FromMinutes(60)，表示60分钟后过期</param>
        /// <returns></returns>
        public static Task SetAsync(string key, object value, TimeSpan? expire = null)
        {
            if (expire.HasValue)
            {
                Set(key, value, expire.Value);
            }
            else
            {
                Set(key, value);
            }
            return Task.CompletedTask;
        }
        #endregion

        #region 设置或获取缓存
        /// <summary>
        /// 设置或获取缓存
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="func">缓存value</param>
        /// <param name="expire">过期时间，例如TimeSpan.FromMinutes(60)，表示60分钟后过期</param>
        /// <returns></returns>
        public static T GetOrSet<T>(string key, Func<T> func, TimeSpan? expire = null)
        {
            #region #region 判断缓存key是否存在,存在执行此处代码
            if (Exists(key))
            {
                try
                {
                    //存在，根据key获取缓存数据并返回
                    return Get<T>(key);
                }
                catch
                {
                    //异常，删除指定key的缓存值
                    Del(key);
                }
            }
            #endregion
            //缓存key不存在，直接返回func的值
            var result = func.Invoke();

            //设置缓存
            if (expire.HasValue)
            {
                Set(key, result, expire.Value);
            }
            else
            {
                Set(key, result);
            }

            return result;
            /*
            调用示例：
            string key1 = "T_GetOrSetTest1";
            string string1 = CoreHelper.MemoryCacheHelper.GetOrSet<string>(key1, () => BLL.IFsql.fsql.Select<Models.users>().Where(a => a.Id == 1).ToOne(a => a.name), TimeSpan.FromMinutes(3));

            string key2 = "T_GetOrSetTest2";
            Models.users _models_users = CoreHelper.MemoryCacheHelper.GetOrSet<Models.users>(key2, () => BLL.IFsql.fsql.Select<Models.users>().Where(a => a.Id == 1).ToOne(), TimeSpan.FromMinutes(3));
            string _users = CoreHelper.SerializeHelper.JsonSerialize<Models.users>(_models_users);
              */
        }


        /// <summary>
        /// 设置或获取缓存
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="key">缓存key</param>
        /// <param name="func">缓存value
        /// func示例() => fsql.Select《Models.SysConfig》().Where(a => a.sysKey == "123").ToOne()
        /// </param>
        /// <param name="expire">过期时间，例如TimeSpan.FromMinutes(60)，表示60分钟后过期</param>
        /// <returns></returns>
        public static async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> func, TimeSpan? expire = null)
        {
            #region #region 判断缓存key是否存在,存在执行此处代码
            if (await ExistsAsync(key))
            {
                try
                {
                    //存在，根据key获取缓存数据并返回
                    return await GetAsync<T>(key);
                }
                catch
                {
                    //异常，删除指定key的缓存值
                    await DelAsync(key);
                }
            }
            #endregion
            //缓存key不存在，直接返回func的值
            var result = await func.Invoke();

            //设置缓存
            if (expire.HasValue)
            {
                await SetAsync(key, result, expire.Value);
            }
            else
            {
                await SetAsync(key, result);
            }

            return result;
            /*
            调用示例：
            string key1 = "T_GetOrSetAsyncTest1";
            string string1 = await CoreHelper.MemoryCacheHelper.GetOrSetAsync<string>(key1, () => BLL.IFsql.fsql.Select<Models.users>().Where(a => a.Id == 1).ToOneAsync(a => a.name), TimeSpan.FromMinutes(3));

            string key2 = "T_GetOrSetAsyncTest2";
            Models.users _models_users = await CoreHelper.MemoryCacheHelper.GetOrSetAsync<Models.users>(key2, () => BLL.IFsql.fsql.Select<Models.users>().Where(a => a.Id == 1).ToOneAsync(), TimeSpan.FromMinutes(3));
            string _users = CoreHelper.SerializeHelper.JsonSerialize<Models.users>(_models_users);
              */
        }
        #endregion

        #region 获取缓存集合
        /// <summary>
        /// 获取所有缓存键
        /// </summary>
        /// <returns></returns>
        public static List<string> GetCacheKeys()
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = _memoryCache.GetType().GetField("_entries", flags).GetValue(_memoryCache);
            var cacheItems = entries as IDictionary;
            var keys = new List<string>();
            if (cacheItems == null) return keys;
            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                keys.Add(cacheItem.Key.ToString());
            }
            return keys;
        }

        /// <summary>
        /// 获取缓存集合,带value
        /// </summary>
        /// <param name="keys">缓存Key集合</param>
        /// <returns></returns>
        public static IDictionary<string, object> GetAll(IEnumerable<string> keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));

            var dict = new Dictionary<string, object>();
            keys.ToList().ForEach(item => dict.Add(item, _memoryCache.Get(item)));
            return dict;
        }
        #endregion
    }
}